#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/select.h>

#define SERVER_IP "192.168.2.87" // 服务器绑定的IP地址
#define SERVER_PORT 60000         // 服务器监听的端口号
#define MAX_CLIENTS 10            // 最大同时连接的客户端数量
#define BUFFER_SIZE 1024          // 消息缓冲区大小

// 客户端状态枚举
typedef enum
{
    CLIENT_UNREGISTERED = 1, // 未注册状态
    CLIENT_REGISTERED,       // 已注册但未登录状态
    CLIENT_LOGGED_IN         // 已登录状态
} client_state;

// 客户端信息结构体
typedef struct
{
    int socket_fd;           // 客户端套接字描述符
    char username[20];       // 客户端用户名
    char password[20];       // 客户端密码
    struct sockaddr_in addr; // 客户端网络地址信息
    client_state state;      // 客户端当前状态
} client_mag;

// 错误处理函数：打印错误信息并退出程序
void handle_error(const char *msg)
{
    perror(msg);
    exit(EXIT_FAILURE);
}

// 初始化服务器：创建套接字、绑定地址、开始监听
int init_server()
{
    int server_fd = socket(AF_INET, SOCK_STREAM, 0);
    if (server_fd == -1)
    {
        handle_error("socket creation failed");
    }

    // 设置端口复用，允许服务器在同一端口快速重启
    int opt = 1;
    if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) == -1)
    {
        handle_error("setsockopt failed");
    }

    // 配置服务器地址并绑定
    struct sockaddr_in server_addr = {
        .sin_family = AF_INET,
        .sin_port = htons(SERVER_PORT),
        .sin_addr.s_addr = inet_addr(SERVER_IP)};

    if (bind(server_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) == -1)
    {
        handle_error("bind failed");
    }

    if (listen(server_fd, 5) == -1)
    {
        handle_error("listen failed");
    }

    return server_fd;
}

// 发送在线客户端列表给指定客户端
void send_client_list(client_mag *clients, int sender_idx)
{
    char list_msg[BUFFER_SIZE] = "Online clients:\n";
    char client_info[50];

    // 遍历所有客户端，收集在线且已登录的客户端信息
    for (int i = 0; i < MAX_CLIENTS; i++)
    {
        if (clients[i].socket_fd != -1 && i != sender_idx && clients[i].state == CLIENT_LOGGED_IN)
        {
            char ip_str[INET_ADDRSTRLEN];
            inet_ntop(AF_INET, &(clients[i].addr.sin_addr), ip_str, INET_ADDRSTRLEN);
            snprintf(client_info, sizeof(client_info), "[%d] %s@%s:%d\n",
                     i, clients[i].username, ip_str, ntohs(clients[i].addr.sin_port));
            strcat(list_msg, client_info);
        }
    }

    // 将客户端列表发送给请求者
    send(clients[sender_idx].socket_fd, list_msg, strlen(list_msg) + 1, 0);
}

// 处理客户端注册请求
void handle_register(client_mag *client, const char *username, const char *password)
{
    // 保存用户名和密码
    strncpy(client->username, username, sizeof(client->username) - 1);
    strncpy(client->password, password, sizeof(client->password) - 1);
    client->state = CLIENT_REGISTERED; // 更新客户端状态为已注册

    // 发送注册成功消息
    char response[BUFFER_SIZE];
    snprintf(response, sizeof(response), "Registration successful! Welcome %s", client->username);
    send(client->socket_fd, response, strlen(response) + 1, 0);
}

// 处理客户端登录请求
int handle_login(client_mag *client, const char *username, const char *password)
{
    // 验证用户名和密码
    if (strcmp(client->username, username) == 0 && strcmp(client->password, password) == 0)
    {
        client->state = CLIENT_LOGGED_IN; // 更新客户端状态为已登录

        // 发送登录成功消息
        char response[BUFFER_SIZE];
        snprintf(response, sizeof(response), "Login successful! Welcome back %s", client->username);
        send(client->socket_fd, response, strlen(response) + 1, 0);
        return 1; // 登录成功
    }

    // 登录失败，发送错误消息
    send(client->socket_fd, "Login failed: Invalid credentials", 34, 0);
    return 0; // 登录失败
}

// 广播消息给所有已登录的客户端（除发送者外）
void broadcast_message(client_mag *clients, int sender_idx, const char *message)
{
    // 格式化消息：添加发送者用户名前缀
    char formatted_msg[BUFFER_SIZE];
    snprintf(formatted_msg, sizeof(formatted_msg), "[%s]: %s",
             clients[sender_idx].username, message);

    // 遍历所有客户端，发送消息
    for (int i = 0; i < MAX_CLIENTS; i++)
    {
        if (i != sender_idx && clients[i].socket_fd != -1 && clients[i].state == CLIENT_LOGGED_IN)
        {
            send(clients[i].socket_fd, formatted_msg, strlen(formatted_msg) + 1, 0);
        }
    }
}

// 发送私聊消息给指定客户端
void send_private_message(client_mag *clients, int sender_idx, int receiver_idx, const char *message)
{
    // 检查接收者索引是否有效
    if (receiver_idx < 0 || receiver_idx >= MAX_CLIENTS || clients[receiver_idx].socket_fd == -1)
    {
        send(clients[sender_idx].socket_fd, "Error: Invalid recipient", 25, 0);
        return;
    }

    // 格式化私聊消息：添加"PM from"前缀
    char formatted_msg[BUFFER_SIZE];
    snprintf(formatted_msg, sizeof(formatted_msg), "[PM from %s]: %s",
             clients[sender_idx].username, message);

    // 发送消息给接收者
    send(clients[receiver_idx].socket_fd, formatted_msg, strlen(formatted_msg) + 1, 0);

    // 通知发送者消息已发送
    char confirmation[BUFFER_SIZE];
    snprintf(confirmation, sizeof(confirmation), "Message sent to [%s]",
             clients[receiver_idx].username);
    send(clients[sender_idx].socket_fd, confirmation, strlen(confirmation) + 1, 0);
}

// 主函数：服务器入口点
int main()
{
    int server_fd = init_server();
    printf("Server started on %s:%d\n", SERVER_IP, SERVER_PORT);

    // 初始化客户端数组，所有位置标记为未使用
    client_mag clients[MAX_CLIENTS];
    for (int i = 0; i < MAX_CLIENTS; i++)
    {
        clients[i].socket_fd = -1;
        clients[i].state = CLIENT_UNREGISTERED;
    }

    fd_set read_fds;        // 用于select的读文件描述符集合
    int max_fd = server_fd; // 当前最大的文件描述符

    // 服务器主循环：持续监听连接和数据
    while (1)
    {
        FD_ZERO(&read_fds);           // 清空文件描述符集合
        FD_SET(server_fd, &read_fds); // 将服务器套接字加入集合

        // 将所有客户端套接字加入集合
        for (int i = 0; i < MAX_CLIENTS; i++)
        {
            if (clients[i].socket_fd != -1)
            {
                FD_SET(clients[i].socket_fd, &read_fds);
                if (clients[i].socket_fd > max_fd)
                {
                    max_fd = clients[i].socket_fd; // 更新最大文件描述符
                }
            }
        }

        // 使用select监听所有套接字的可读事件
        int activity = select(max_fd + 1, &read_fds, NULL, NULL, NULL);
        if (activity < 0 && errno != EINTR)
        {
            handle_error("select error");
        }

        // 处理新连接请求
        if (FD_ISSET(server_fd, &read_fds))
        {
            struct sockaddr_in client_addr;
            socklen_t addr_len = sizeof(client_addr);
            int client_fd = accept(server_fd, (struct sockaddr *)&client_addr, &addr_len);
            if (client_fd == -1)
            {
                perror("accept failed");
                continue;
            }

            // 寻找空闲位置存储新客户端信息
            int i;
            for (i = 0; i < MAX_CLIENTS; i++)
            {
                if (clients[i].socket_fd == -1)
                {
                    clients[i].socket_fd = client_fd;
                    clients[i].addr = client_addr;
                    clients[i].state = CLIENT_UNREGISTERED;
                    break;
                }
            }

            // 如果没有空闲位置，拒绝连接
            if (i == MAX_CLIENTS)
            {
                send(client_fd, "Server is full", 14, 0);
                close(client_fd);
                continue;
            }

            // 打印新连接信息并发送欢迎消息
            char ip_str[INET_ADDRSTRLEN];
            inet_ntop(AF_INET, &(client_addr.sin_addr), ip_str, INET_ADDRSTRLEN);
            printf("New connection from %s:%d assigned ID %d\n",
                   ip_str, ntohs(client_addr.sin_port), i);

            send(client_fd, "Welcome to the chat server!\nPlease register or login", 54, 0);
        }

        // 处理客户端数据
        for (int i = 0; i < MAX_CLIENTS; i++)
        {
            if (clients[i].socket_fd != -1 && FD_ISSET(clients[i].socket_fd, &read_fds))
            {
                char buffer[BUFFER_SIZE] = {0};
                int bytes_read = recv(clients[i].socket_fd, buffer, BUFFER_SIZE, 0);

                if (bytes_read <= 0)
                {
                    // 客户端断开连接，清理资源
                    printf("Client %d disconnected\n", i);
                    close(clients[i].socket_fd);
                    clients[i].socket_fd = -1;
                    clients[i].state = CLIENT_UNREGISTERED;
                    continue;
                }

                // 处理客户端消息
                buffer[bytes_read] = '\0';

                // 根据客户端状态执行不同的处理逻辑
                if (clients[i].state == CLIENT_UNREGISTERED)
                {
                    // 未注册客户端，处理注册请求
                    if (strncmp(buffer, "register", 8) == 0)
                    {
                        char *space = strchr(buffer, ' '); // 查找第一个空格
                        if (space)
                        {
                            *space = '\0';                                    // 分割字符串
                            char *username = buffer + 9;                      // "register"后移9字节
                            char *password = space + 1;                       // 第一个空格后的内容
                            handle_register(&clients[i], username, password); // 处理注册
                        }
                        else
                        {
                            send(clients[i].socket_fd, "Invalid register format. Use: register <username> <password>", 55, 0);
                        }
                    }
                    else
                    {
                        send(clients[i].socket_fd, "Please register first: register <username> <password>", 52, 0);
                    }
                }
                else if (clients[i].state == CLIENT_REGISTERED)
                {
                    // 已注册但未登录，处理登录请求
                    if (strncmp(buffer, "login", 5) == 0)
                    {
                        char *space = strchr(buffer, ' ');
                        if (space)
                        {
                            *space = '\0';
                            char *username = buffer + 6; // "login"后移6字节
                            char *password = space + 1;
                            if (handle_login(&clients[i], username, password))
                            {
                                send_client_list(clients, i); // 登录成功后发送在线用户列表
                            }
                        }
                        else
                        {
                            send(clients[i].socket_fd, "Invalid login format. Use: login <username> <password>", 47, 0);
                        }
                    }
                    else
                    {
                        send(clients[i].socket_fd, "Please login: login <username> <password>", 40, 0);
                    }
                }
                else if (clients[i].state == CLIENT_LOGGED_IN)
                {
                    // 已登录客户端，处理各种命令和消息
                    if (strcmp(buffer, "list") == 0)
                    {
                        send_client_list(clients, i); // 请求在线用户列表
                        printf("Sent client list to client %d\n", i);
                    }
                    else if (strncmp(buffer, "pm ", 3) == 0)
                    {
                        // 处理私聊消息：格式为 "pm <client_id> <message>"
                        char *first_space = strchr(buffer, ' ');
                        if (first_space)
                        {
                            char *second_space = strchr(first_space + 1, ' ');
                            if (second_space)
                            {
                                *second_space = '\0';                    // 分割字符串
                                int receiver_id = atoi(first_space + 1); // 提取接收方ID
                                char *message = second_space + 1;        // 提取消息内容
                                send_private_message(clients, i, receiver_id, message);
                                printf("Client %d sent PM to client %d: %s\n", i, receiver_id, message);
                            }
                            else
                            {
                                send(clients[i].socket_fd, "Invalid private message format. Use: pm <client_id> <message>", 59, 0);
                            }
                        }
                        else
                        {
                            send(clients[i].socket_fd, "Invalid private message format. Use: pm <client_id> <message>", 59, 0);
                        }
                    }
                    else
                    {
                        // 默认作为广播消息处理
                        broadcast_message(clients, i, buffer);
                        printf("Client %d broadcast: %s\n", i, buffer);
                    }
                }
            }
        }
    }
    close(server_fd); // 关闭服务器套接字
    return 0;
}